// initialization
var express = require('express');
var app = express();
//// for https
var fs = require('fs');
var privateKey = fs.readFileSync('private.pem', 'utf8');
var certificate = fs.readFileSync('file.crt', 'utf8');
var credentials = {key: privateKey, cert: certificate};

var bodyParser= require('body-parser');
var morgan = require('morgan');
var mongoose = require('mongoose');

var jwt = require('jsonwebtoken');
var config = require('./config');
var User = require('./app/models/user');

// crypto
const crypto = require('crypto');

//configuration
var port = process.env.PORT || 9000;
mongoose.connect(config.database);
app.set('superSecret', config.secret);

// use body parser so we can get info from POST and/or URL parameters
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// use morgan to log requests to the console
app.use(morgan('dev'));

// ========================
// routes =================
// ========================

// basic route
app.get('/', function(req, res) {
    res.send('Hello! The API is at 101.132.70.210' + port + '/api');
});

// API routes
var apiRoutes = express.Router();
apiRoutes.get('/', function(req, res) {
    res.json({message: 'Welcome to the API module.'});
});

// eg. by using PostMan, u need to use 'body' | x-www-form-urlencoded, key: value as 'name: xxx', 'password: xxx'
apiRoutes.post('/register', function(req, res) {
    User.findOne({
        name: req.body.name
    }, function(err, user) {
        if (err) throw err;

        if (user) {
            res.json({success: false, message: 'User already exist!'});
        } else if (!user) {
            const buf = crypto.randomBytes(32); //we need 256bits: 32bytes * 8bit/byte

            function toHexString(byteArray) {
                return Array.from(byteArray, function(byte) {
                    return ('0' + (byte & 0xFF).toString(16).slice(-2));
                }).join('');
            }

            const salt = toHexString(buf);
            //console.log(salt.toString('hex'));
            
            var target = req.body.password + salt;
            var hash = crypto.createHash('sha256').update(target).digest('base64');
            var user = new User({name:req.body.name, password:hash, admin: false, salt: salt, token:""});
            user.save(function(err){
                if (err) throw err;
                console.log('[Register] user register successfully: ' + req.body.name);
                res.json({ success: true});
            });
        }
    });
});

apiRoutes.post('/login', function(req, res) {
    User.findOne({
        name: req.body.name
    }, function(err, user) {
        if (err) throw err;
        
        if (!user) {
            res.json({success: false, message: 'User name not found!'});
        } else {
            var target = req.body.password + user.salt;
            var hash = crypto.createHash('sha256').update(target).digest('base64');
            
            //compare the hash with that in database
            if (user.password == hash) {
                //generate a token for this user
                const payload = {
                    name: user.name,
                };
                
                var token = jwt.sign(payload, app.get('superSecret'), {
                    expiresIn: 24 * 7 * 60 * 60
                });

                //var usernow = new User({name:user.name, password:user.password, admin:user.admin, salt:user.salt, token: token});
                //usernow.save(function(err) {
                //    if(err) throw err;
                //});
                User.findOneAndUpdate({name: user.name}, {$set:{token: token}}, {new: false}, function(err, doc) {
                        if (err) console.log('sth wrong to update database.');
                    } 
                );

                res.json({ success: true, message: 'User login.', token: token });
            }
            else
                res.json({success: false, message: 'Password is not correct.'}); 
        }
    }); 
});

apiRoutes.post('/autologin', function(req, res) {
    User.findOne({
        name: req.body.name
    }, function(err, user) {
        if (err) throw err;

        if (!user) {
            res.json({success: false, message: 'User name not found!'});
        }
        else {
            //check token
            if (!req.body.token) {
                res.json({success: false, message: 'No token supplied!'});
            }
            else if (req.body.token != user.token) {
                res.json({success: false, message: 'Token is not valid!'});
            }
            else {
                jwt.verify(req.body.token, app.get('superSecret'), function(error, decoded) {
                    if (err) {
                        if (err.name == 'TokenExpiredError') {
                            res.json({success: false, message: 'Token expired.'});
                        }
                        else {
                            res.json({success: false, message: 'Token error.'});
                        }
                    }
                    else {
                        res.json({success: true, message: 'User login.'});
                    }
                });
                
            }
        }
    });
});

app.use('/api', apiRoutes);

// ================================
// start server now, http and https
// https@port:8443, http@port:8081
// ================================
var http = require('http');
var https = require('https');

var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);

httpServer.listen(8081);
httpsServer.listen(8443);

//app.listen(port); #if we only use http, this line is enough
